package com.six.utils;

import com.six.entity.MinioBean;
import io.minio.*;
import io.minio.errors.*;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.IOUtils;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * ClassName:MinioUtil
 * Package:com.six.utils
 * Description
 *
 * @Author:@wenxueshi
 * @Create:2023/7/3 - 22:08
 * @Version:v1.0
 */
@Slf4j
@Component
public class MinioUtil {

    @Autowired
    private MinioClient minioClient;

    @Value("${minio.bucketName}")
    private String bucketName;

    @Value("${minio.endpoint}")
    private String endpoint;

    /**
     * 判断bucket是否存在
     * @param name
     */
    public boolean existBucket(String name){
        //根据bucketName查看是否存在
        try {
            boolean exists = minioClient.bucketExists(BucketExistsArgs.builder()
                    .bucket(name)
                    .build());
            if(!exists){
                return false;
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return true;
    }

    /**
     * 创建存储bucketName
     * @param name
     * @return
     */
    public boolean makeBucket(String name){
        try {
            minioClient.makeBucket(MakeBucketArgs.builder()
                    .bucket(name)
                    .build());
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 删除存储bucketName
     * @param name
     * @return
     */
    public boolean removeBucket(String name){
        try {
            minioClient.removeBucket(RemoveBucketArgs.builder()
                    .bucket(name)
                    .build());
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 删除指定文件
     * @param name
     * @param file
     */
    public boolean deleteFile(String name,String file){
        try {
            minioClient.removeObject(RemoveObjectArgs.builder().bucket(name).object(file).build());
        }catch (Exception e){
            return false;
        }
        return true;
    }

    /**
     * description: 上传文件
     *
     * @param multipartFile
     * @return: java.lang.String
     */
    public List<String> upload(MultipartFile[] multipartFile) {
        List<String> names = new ArrayList<>(multipartFile.length);
        for (MultipartFile file : multipartFile) {
            //获取文件原始名：2.jpg
            String fileName = file.getOriginalFilename();
            //必须写出\\.这是转义，将文件名从.开始分割成 2 和 jpg
            String[] split = fileName.split("\\.");
            if (split.length > 1) {
                //防止文件重名：添加系统时间：文件名为：2_37598375837.jpg
                //方式1：
                //fileName = split[0] + "_" + System.currentTimeMillis() + "." + split[1];
                //方式2：
                // 完善1、 --> 在文件名中添加唯一值
                String uuid = UUID.randomUUID().toString().replace("-", "");
                fileName = uuid + split[0] + "." + split[1];
                // 完善2、 --> 把文件按照日期分类
                String datePath = new DateTime().toString("yyyy/MM/dd");
                // 拼接时间 yyyy/MM/dd/filename
                fileName = datePath + "/" + fileName;
            } else {
                fileName = fileName + System.currentTimeMillis();
            }
            InputStream in = null;
            try {
                //开始上传
                in = file.getInputStream();
                minioClient.putObject(PutObjectArgs.builder()
                        .bucket(bucketName)
                        .object(fileName)
                        .stream(in, in.available(), -1)
                        .contentType(file.getContentType())
                        .build()
                );
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                //关闭流
                if (in != null) {
                    try {
                        in.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            fileName = endpoint + "/" + bucketName + "/" + fileName;
            names.add(fileName);
        }
        //返回图片地址url
        return names;
    }

    /**
     * description: 下载文件
     *
     * @param fileName
     * @return: org.springframework.http.ResponseEntity<byte [ ]>
     */
    public ResponseEntity<byte[]> download(String fileName) {
        ResponseEntity<byte[]> responseEntity = null;
        InputStream in = null;
        ByteArrayOutputStream out = null;
        try {
            //通过文件名获取
            in = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(fileName).build());
            out = new ByteArrayOutputStream();
            IOUtils.copy(in, out);
            //封装返回值
            byte[] bytes = out.toByteArray();
            HttpHeaders headers = new HttpHeaders();
            try {
                headers.add("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            headers.setContentLength(bytes.length);
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
            headers.setAccessControlExposeHeaders(Arrays.asList("*"));
            responseEntity = new ResponseEntity<byte[]>(bytes, headers, HttpStatus.OK);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (in != null) {
                    try {
                        in.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return responseEntity;
    }

    /**
     * 查看文件对象
     * @param bucketName 存储bucket名称
     * @return 存储bucket内文件对象信息
     */
    public List<MinioBean> listObjects(String bucketName) {
        Iterable<Result<Item>> results = minioClient.listObjects(
                ListObjectsArgs.builder().bucket(bucketName).build());
        List<MinioBean> objectItems = new ArrayList<>();
        try {
            for (Result<Item> result : results) {
                Item item = result.get();
                MinioBean objectItem = new MinioBean();
                objectItem.setName(item.objectName());
                objectItem.setSize(item.size());
                objectItems.add(objectItem);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return objectItems;
    }

    /**
     * 批量删除文件对象
     * @param bucketName 存储bucket名称
     * @param fileNames 对象名称集合
     */
    public boolean batchDeleteBucket(String bucketName, List<String> fileNames) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        String file = fileNames.get(0);
        minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(file).build());
        return true;
    }
}
